from pathlib import Path
import datetime as dt
import polars as pl

from fastapi import (
    FastAPI, 
    APIRouter,
    Depends,
    Body,
)

from .schema import KlineQuery, FiledQuery

class FactorApi:
    KlineCol = set(["open", "high", "low", "close", "volume", "turnover"])
    def router():
        router = APIRouter(
            prefix = "/api/factor",
            tags = ["因子信息"],
            responses = {404: {"description": "Not found"}},
        )
        
        @router.post("/queryDailyNv", 
                        summary="获取日频费后收益")
        async def query_daily_nv(path:str = Body(...,embed=True)):
            success = False
            data = []
            msg = ""
            try:
                if path == "":
                    success = False
                    msg = "路径不能为空"
                elif not Path(path).exists():
                    success = False
                    msg = f"{path}: 路径不存在"
                else:
                    workspace = Path(path)
                    # 读取其中的日频绩效
                    dailyNv = workspace.joinpath("daily.csv")
                    if not dailyNv.exists():
                        success = False
                        msg = "日频绩效文件不存在"
                    else:
                        df = pl.scan_csv(dailyNv.as_posix())
                        xy = df.select(["trading_date", "cum_nv"]).collect()
                        x = xy["trading_date"].to_list()
                        y = xy["cum_nv"].to_list()
                        data = {
                            "x":x,
                            "y":y
                        }

                    success = True
            except Exception as e:
                success = False
                msg = f"获取日频费后收益失败: {str(e)}"
                
            return {
                "success": success,
                "data": data,
                "msg": msg
            }
    
        @router.post("/queryStats", 
                        summary="获取绩效统计")
        async def query_stats(
            path:str = Body(...,embed=True)
            ):
            success = False
            data = []
            msg = ""
            try:
                if path == "":
                    success = False
                    msg = "路径不能为空"
                elif not Path(path).exists():
                    success = False
                    msg = f"{path}: 路径不存在"
                else:
                    workspace = Path(path)
                    # 读取其中的绩效统计
                    stats = workspace.joinpath("stats.csv")
                    if not stats.exists():
                        success = False
                        msg = f"绩效统计文件{path}不存在"
                    else:
                        df = pl.read_csv(stats.as_posix()).fill_null(0)
                        data = dict(zip(df["id"], df["value"].round(2)))
                        if "version" not in data:
                            data["version"] = 1

                        success = True
            except Exception as e:
                success = False
                msg = f"获取绩效统计失败: {str(e)}"
                
            return {
                "success": success,
                "data": data,
                "msg": msg
            }
            
        @router.post("/readKlineData",
                     summary="读取K线数据")
        async def read_kline_data(
            query:KlineQuery
        ):
            success = False
            data = []
            msg = ""
            try:
                if not Path(query.filePath).exists():
                    success = False
                    msg = f"{query.filePath}: 路径不存在"
                else:
                    while True:
                        # 读取带有K线数据的因子数据
                        df = pl.scan_csv(query.filePath)
                        schema = df.collect_schema()
                        schemaDict = {k:v if v != pl.Int64 else pl.Float64 for k,v in schema.items()}
                        columns = schema.names()
                        df = pl.scan_csv(query.filePath, schema=schemaDict)
                        # 提取指定的数据
                        df = df.filter(pl.col("underlying") == query.underlying).sort("datetime")
                        if query.sDate is not None:
                            df = df.filter(pl.col("trading_date") >= query.sDate)
                        if query.eDate is not None:
                            df = df.filter(pl.col("trading_date") <= query.eDate)

                        # 字段转换
                        colMap = {}
                        if "datetime" in columns:
                            df = df.with_columns(
                                timestamp = pl.col("datetime").str.to_datetime().cast(pl.Int64) // 1000
                            )
                        else:
                            success = False
                            msg = "缺少datetime字段"
                            break
                        col = list(set(columns) & FactorApi.KlineCol)
                        col.append("timestamp")
                        df = df.select(
                            col
                        )
                        df = df.collect()
                        col = df.columns
                        for row in df.iter_rows():
                            data.append(
                                dict(zip(col, row))
                            )
                            
                        break
                    success = True
            except Exception as e:
                success = False
                msg = f"从{query.filePath}读取K线数据: {str(e)}"
                
            return {
                "success": success,
                "data": data,
                "msg": msg
            }
            
        @router.post("/readFieldData",
                     summary="读取字段数据")
        async def read_field_data(
            query:FiledQuery,
        ):
            success = False
            data = {}
            msg = ""
            try:
                if not Path(query.filePath).exists():
                    success = False
                    msg = f"{query.filePath}: 路径不存在"
                else:
                    # 读取带有K线数据的因子数据
                    df = pl.scan_csv(query.filePath)
                    schema = df.collect_schema()
                    schemaDict = {k:v if v != pl.Int64 else pl.Float64 for k,v in schema.items()}
                    df = pl.scan_csv(query.filePath, schema=schemaDict)
                    # 提取指定的数据
                    df = df.filter(pl.col("underlying") == query.underlying).sort("datetime")
                    df = df.select(
                        query.field
                    )
                    df = df.collect()
                    data = {k:v.to_list() for k,v in df.to_dict().items()}
                    
                    success = True
            except Exception as e:
                success = False
                msg = f"从{query.filePath}读取字段{query.field}失败: {str(e)}"
                
            return {
                "success": success,
                "data": data,
                "msg": msg
            }
            
        @router.post("/readUdList",
                     summary="读取品种信息")
        async def read_ud_list(
            filePath:str = Body(...,embed=True)
            ):
            success = False
            data = {}
            msg = ""
            try:
                if not Path(filePath).exists():
                    success = False
                    msg = f"{filePath}: 路径不存在"
                else:
                    # 读取带有K线数据的因子数据
                    df = pl.scan_csv(filePath)
                    # 提取指定的数据
                    df = df.select(r = pl.col("underlying")).unique()
                    df = df.collect()
                    data = df["r"].sort().to_list()
                    
                    success = True
            except Exception as e:
                success = False
                msg = f"从{filePath}读取品种信息失败: {str(e)}"
                
            return {
                "success": success,
                "data": data,
                "msg": msg
            }
        return router
    
    
    